"""
每次向服务器请求数据都需要三次握手建立连接，四次挥手断开连接，比较耗时，成为短连接
一次三次握手多次数据传输为长连接
"""
import socket
import re


# 实现单进程、单线程、非堵塞、长连接的服务器
def do_service(client_socket, request):
    """
    处理客户端请求
    :param client_socket: 客户端socket
    :param request: 客户端发来的数据
    :return:
    """
    request_line = request.splitlines()
    ret = re.match(r"[^/]+(/[^ ]*)", request_line[0])
    if ret:
        file_name = ret.group(1)
        if file_name == "/":
            file_name = "/index.html"
        if file_name.endswith(".png") or file_name.endswith("jpg"):
            file_name = "/images/trolltech-logo.png"

    try:
        file = open("../html" + file_name, "rb")
    except Exception as ret:
        # 相应404
        response = "HTTP/1.1 404 NOT FOUND \r\n"
        response += "\r\n"
        response += "----404----"
        client_socket.send(response.encode("gbk"))
    else:
        response_body = file.read()
        file.close()
        # 封装响应头数据
        # 由于使用长连接，如果不调用close则浏览器会一直转圈等待服务器返回数据，那么客户端如果知晓数据传输完成
        # 此时就需要添加响应头 Content-Length
        response_header = "HTTP/1.1 200 \r\n"
        response_header += "Content_Length:%d\r\n" % len(response_body)
        response_header += "\r\n"

        # response body为文件读出的二进制流不能和字符串的response_header直接相加
        response = response_header.encode("gbk") + response_body
        client_socket.send(response)

    # 由于是长连接，此处不能关闭socket只有等待浏览器主动断开连接才能调用close方法
    # client_socket.close()


def main():
    tcp_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp_socket.bind(("", 7890))
    tcp_socket.listen(128)
    # socket默认为堵塞式等待接收连接，有连接则返回客户端socket，设置为非堵塞式
    tcp_socket.setblocking(False)
    client_socket_list = list()
    while True:
        # 由于socket设置为非堵塞式，如果没有客户端连接则报异常
        try:
            client_socket, client_addr = tcp_socket.accept()
        except Exception as ret:
            pass
        else:
            client_socket.setblocking(False)
            client_socket_list.append(client_socket)

        # 遍历集合，如果有客户端则通讯
        for client_socket in client_socket_list:
            try:
                request = client_socket.recv(1024)
            except Exception as ret:
                pass
            else:
                if request:
                    do_service(client_socket, request.decode("gbk"))
                else:
                    # 如果取得的数据request为空，表示客户端此处为浏览器断开连接，此时才可以关闭客户端的socket
                    client_socket.close()
                    client_socket_list.remove(client_socket)


if __name__ == '__main__':
    main()
